#include "ModelLoader.h"

ModelLoader::~ModelLoader()
{
	for (map<char*, EnginePointer<Model>>::iterator it = loadedModels.begin(); it != loadedModels.end(); it++)
	{
		free((*it).first);
	}
}

HRESULT ModelLoader::LoadModel(char* path, EnginePointer<Model>& model)
{
	for (map<char*, EnginePointer<Model>>::iterator it = loadedModels.begin(); it != loadedModels.end(); it++)
	{
		if (strcmp((*it).first, path) == 0)
		{
			model = (*it).second;
			return S_OK;
		}
	}

	HANDLE fileHandle = CreateFileA(path, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	if (fileHandle == INVALID_HANDLE_VALUE) return E_FAIL;
	LARGE_INTEGER fileSize;
	GetFileSizeEx(fileHandle,& fileSize);
	BYTE* modelData = new BYTE[fileSize.LowPart];

	DWORD bytesRead;
	ReadFile(fileHandle, modelData, fileSize.LowPart,& bytesRead, NULL);

	CloseHandle(fileHandle);

	HRESULT hr = LoadModel(modelData, model);

	DWORD pathLength = strlen(path) + 1;
	char* pathCopy = (char*)malloc(pathLength);
	strcpy_s(pathCopy, pathLength, path);
	loadedModels[pathCopy] = model;

	delete modelData;

	return hr;
}

HRESULT ModelLoader::LoadModel(int resource, EnginePointer<Model>& model)
{
	HGLOBAL data = LoadResource(GetModuleHandle(L"D3D10enginecontent.dll"), FindResource(GetModuleHandle(L"D3D10enginecontent.dll"), MAKEINTRESOURCE(resource), RT_RCDATA));

	BYTE* modelData = (BYTE*)LockResource(data);

	HRESULT hr = LoadModel(modelData, model);

	UnlockResource(modelData);

	return hr;
}

HRESULT ModelLoader::LoadModel(BYTE* data, EnginePointer<Model>& model)
{
	HRESULT hr;
	
	SDKMESH mesh;
	hr = FillSdkMesh(&mesh, data);
	if (FAILED(hr)) return hr;

	ConvertToModel(&mesh, model);

	for (UINT i = 0; i < mesh.meshHeader->NumIndexBuffers; i++)
	{
		delete mesh.indexDatas[i];
	}
	delete mesh.indexDatas;
	for (UINT i = 0; i < mesh.meshHeader->NumVertexBuffers; i++)
	{
		delete mesh.vertexDatas[i];
	}
	delete mesh.vertexDatas;
	for (UINT i = 0; i < mesh.meshHeader->NumMeshes; i++)
	{
		delete mesh.meshes[i].pSubsets;
	}
	delete mesh.materials;
	delete mesh.subsets;
	delete mesh.meshes;
	delete mesh.indexHeaders;
	delete mesh.vertexHeaders;
	delete mesh.meshHeader;

	return S_OK;
}

HRESULT ModelLoader::FillSdkMesh(SDKMESH* mesh, BYTE* modelData)
{
	mesh->meshHeader = new SDKMESH_HEADER;
	memcpy(mesh->meshHeader, modelData, sizeof(SDKMESH_HEADER));
	mesh->vertexHeaders = new SDKMESH_VERTEX_BUFFER_HEADER[mesh->meshHeader->NumVertexBuffers];
	memcpy(mesh->vertexHeaders, modelData + mesh->meshHeader->VertexStreamHeadersOffset, sizeof(SDKMESH_VERTEX_BUFFER_HEADER) * mesh->meshHeader->NumVertexBuffers);
	mesh->indexHeaders = new SDKMESH_INDEX_BUFFER_HEADER[mesh->meshHeader->NumIndexBuffers];
	memcpy(mesh->indexHeaders, modelData + mesh->meshHeader->IndexStreamHeadersOffset, sizeof(SDKMESH_INDEX_BUFFER_HEADER) * mesh->meshHeader->NumIndexBuffers);
	mesh->meshes = new SDKMESH_MESH[mesh->meshHeader->NumMeshes];
	memcpy(mesh->meshes, modelData + mesh->meshHeader->MeshDataOffset, sizeof(SDKMESH_MESH) * mesh->meshHeader->NumMeshes);
	mesh->subsets = new SDKMESH_SUBSET[mesh->meshHeader->NumMeshes];
	memcpy(mesh->subsets, modelData + mesh->meshHeader->SubsetDataOffset, sizeof(SDKMESH_SUBSET) * mesh->meshHeader->NumMeshes);
	mesh->materials = new SDKMESH_MATERIAL[mesh->meshHeader->NumMaterials];
	memcpy(mesh->materials, modelData + mesh->meshHeader->MaterialDataOffset, sizeof(SDKMESH_MATERIAL) * mesh->meshHeader->NumMaterials);

	for( UINT i = 0; i < mesh->meshHeader->NumMeshes; i++ )
    {
		UINT64 offset = mesh->meshes[i].SubsetOffset;
		mesh->meshes[i].pSubsets = new UINT[mesh->meshes[i].NumSubsets];
		memcpy(mesh->meshes[i].pSubsets, modelData + offset, sizeof(UINT) * mesh->meshes[i].NumSubsets);
    }

	mesh->vertexDatas = new BYTE*[mesh->meshHeader->NumVertexBuffers];
	for( UINT i = 0; i < mesh->meshHeader->NumVertexBuffers; i++ )
	{
		mesh->vertexDatas[i] = new BYTE[(size_t)mesh->vertexHeaders[i].SizeBytes];
		memcpy(mesh->vertexDatas[i], modelData + mesh->vertexHeaders[i].DataOffset, (size_t)mesh->vertexHeaders[i].SizeBytes);
	}

	mesh->indexDatas = new BYTE*[mesh->meshHeader->NumIndexBuffers];
    for( UINT i = 0; i < mesh->meshHeader->NumIndexBuffers; i++ )
    {
        mesh->indexDatas[i] = new BYTE[(size_t)mesh->indexHeaders[i].SizeBytes];
		memcpy(mesh->indexDatas[i], modelData + mesh->indexHeaders[i].DataOffset, (size_t)mesh->indexHeaders[i].SizeBytes);
    }

	return S_OK;
}

void ModelLoader::ConvertToModel(SDKMESH* mesh, EnginePointer<Model>& model)
{
	vector<EnginePointer<ModelMesh>>* meshes = new vector<EnginePointer<ModelMesh>>();
	vector<EnginePointer<MeshSubset>>* subsets = new vector<EnginePointer<MeshSubset>>();
	vector<EnginePointer<ModelVertexBuffer>>* vertexBuffers = new vector<EnginePointer<ModelVertexBuffer>>();
	vector<EnginePointer<ModelIndexBuffer>>* indexBuffers = new vector<EnginePointer<ModelIndexBuffer>>();
	vector<EnginePointer<Material>>* materials = new vector<EnginePointer<Material>>();

	for (UINT i = 0; i < mesh->meshHeader->NumVertexBuffers; i++)
	{
		EnginePointer<ModelVertexBuffer> vbPtr;
		vbPtr = new ModelVertexBuffer(mesh->vertexDatas[i], mesh->vertexHeaders[i].NumVertices, mesh->vertexHeaders[i].SizeBytes, mesh->vertexHeaders[i].StrideBytes);
		vertexBuffers->push_back(vbPtr);
	}
	for (UINT i = 0; i < mesh->meshHeader->NumIndexBuffers; i++)
	{
		EnginePointer<ModelIndexBuffer> ibPtr;
		ibPtr = new ModelIndexBuffer(mesh->indexDatas[i], mesh->indexHeaders[i].NumIndices, mesh->indexHeaders[i].SizeBytes, mesh->indexHeaders[i].IndexType);
		indexBuffers->push_back(ibPtr);
	}
	for (UINT i = 0; i < mesh->meshHeader->NumMaterials; i++)
	{
		EnginePointer<Material> mPtr;
		mPtr = new Material(mesh->materials[i].Name, mesh->materials[i].DiffuseTexture, mesh->materials[i].NormalTexture, mesh->materials[i].SpecularTexture);
		materials->push_back(mPtr);
	}
	for (UINT i = 0; i < mesh->meshHeader->NumTotalSubsets; i++)
	{
		EnginePointer<MeshSubset> sPtr;
		sPtr = new MeshSubset(mesh->subsets[i].Name,  materials->at(mesh->subsets[i].MaterialID), mesh->subsets[i].PrimitiveType,
			                  mesh->subsets[i].IndexStart, mesh->subsets[i].IndexCount, mesh->subsets[i].VertexStart, mesh->subsets[i].VertexCount);
		subsets->push_back(sPtr);
	}
	for (UINT i = 0; i < mesh->meshHeader->NumMeshes; i++)
	{
		EnginePointer<ModelMesh> mPtr;
		mPtr = new ModelMesh(mesh->meshes[i].Name, mesh->meshes[i].NumVertexBuffers, vertexBuffers, mesh->meshes[i].VertexBuffers,
			                 indexBuffers->at(mesh->meshes[i].IndexBuffer), mesh->meshes[i].BoundingBoxCenter, mesh->meshes[i].BoundingBoxExtents,
							 mesh->meshes[i].NumSubsets, subsets, mesh->meshes[i].pSubsets);
		meshes->push_back(mPtr);
	}
	model = new Model(meshes, subsets, vertexBuffers, indexBuffers, materials);
}